/**
Header file to describe the programmable state
of the 14-bit instruction word Microchip PIC microcontrollers, 
such as the pic12F675 or the pic16F676.

Orion Sky Lawlor, olawlor@acm.org, 2003/8/3
*/
#include "pic14.h"
#include "hex.h"

/** Initialize to a reasonable power-on value */
void pic14_state_init(pic14_state *p) {
	unsigned int i;
	for (i=0;i<pic14_inst_len;i++) p->program.inst[i]=0x3fffu;
	for (i=0;i<pic14_ee_len;i++) p->program.ee[i]=0xffu;
	p->config.config=0x184u; // Disable watchdog and code protect, enable intosc
	for (i=0;i<pic14_id_len;i++) p->config.id[i]=0x3fffu;
	p->config.osccal=0x2000u;
}

/** Extract a list of spans from this program. */
void pic14_program_spans(pic14_state *p,pic14_span s[]) {
	s[0].desc="Program (14-bit instruction words)";
	s[0].addr=0x0000u;
	s[0].len =pic14_inst_len;
	s[0].data=p->program.inst;
	
	s[1].desc="EEPROM (8-bit data)";
	s[1].addr=0x2100u; /* only meaningful in Intel MDS HEX file */
	s[1].len =pic14_ee_len;
	s[1].data=p->program.ee;
	
	s[2].desc="Configuration word";
	s[2].addr=0x2007u;
	s[2].len =1;
	s[2].data=&p->config.config;
	
	s[3].desc="User ID words";
	s[3].addr=0x2000u;
	s[3].len =pic14_id_len;
	s[3].data=p->config.id;
	
	s[4].desc="OSCCAL";
	s[4].addr=0x03ffu;
	s[4].len =1;
	s[4].data=&p->config.osccal;
}

typedef unsigned char byte;

/** Write this word wherever it belongs in this span list */
void pic14_write_word(pic14_span *spans,unsigned int addr,pic14_word w) {
	unsigned int s;
	for (s=0;s<pic14_program_nspans;s++) 
	if ((addr>=spans[s].addr) && (addr<spans[s].addr+spans[s].len))
	{ /* This is our span-- write us into it */
		spans[s].data[addr-spans[s].addr]=w;
		return;
	}
}

/** Accept this segment of a pic14 program from a .hex file,
    in which everything is stored as *bytes*, not words.
*/
void pic14_hex_segment(void *vp,unsigned int baddr,unsigned int blen,byte *src) 
{
	pic14_state *p=(pic14_state *)vp;
	unsigned int i, addr=baddr/2, len=blen/2;
	pic14_span spans[pic14_program_nspans];
	pic14_program_spans(p,spans);
	for (i=0;i<len;i++)
		pic14_write_word(spans,addr+i,src[2*i+0]+(src[2*i+1]<<8));
}

/** Read this program from a .hex file */
const char *pic14_hex_read(pic14_state *p,FILE *src) {
	// Zero out the state first, so anything that isn't read
	//  won't be uninitialized.
	pic14_state_init(p);
	return hex_read(src,pic14_hex_segment,p);
}

/** Write this program to a .hex file */
void pic14_hex_write(pic14_state *p,FILE *dest) {
	pic14_span spans[pic14_program_nspans];
	int s;
	pic14_program_spans(p,spans);
	hex_write_begin(dest);
	for (s=0;s<pic14_program_nspans;s++) {
		/* Must convert address and data from words to bytes: */
		byte data[2*pic14_inst_len];
		unsigned int addr=2*spans[s].addr;
		unsigned int w,len=2*spans[s].len;
		for (w=0u;w<spans[s].len;w++) {
			unsigned int v=spans[s].data[w];
			data[2*w+0]=(byte)(0xff&v); /* Low end first */
			data[2*w+1]=(byte)(0xff&(v>>8)); /* High end second */
		}
		hex_write(dest,addr,len,data);
	}
	hex_write_end(dest);
}
